#include <yatak/debug.h>
#include <yatak/kheap.h>
#include <yatak/tty.h>
#include <screen.h>
#include <types.h>
#include <sched.h>
#include <libcmos/inc/setjmp.h>

//#define DEBUG_TTY

#ifndef DEBUG_TTY
#define dprintf
#define DBGPRINTF(s)
#endif

extern volatile task_t *current_task;
extern volatile task_t *ready_queue;

struct tty_list
{
	uint32 tty;
	struct tty_list * next;
};
struct tty_list * free_tty = NULL;

static uint32 next_tty = 0;

uint32 pop_tty ()
{
	if (free_tty)
	{
		uint32 pid = free_tty->tty;
		struct tty_list * tmp = free_tty;
		free_tty = free_tty->next;
		kfree (tmp);
		return pid;
	}
	return 0;
}

uint32 findnumber (char * name)
{
	char str [64];
	strcpy(str, name);
	str [3] = '\0';
	if (strcmp(str,"tty"))
		return 0;
	strcpy(str, name);
	int i, j;
	for (i = 3, j = 0; i<strlen(str);i++,j++)
		str [j] = str [i];
	str [j] = '\0';
	return atoi(str);
}
void push_tty (char * name)
{
	uint32 tty = findnumber (name);
	if (!tty)
		return;
	struct tty_list * tmp = free_tty;
	while (tmp)
	{
		if (tmp->tty == tty)
			return; 
		tmp = tmp->next;
	}
	tmp = (struct tty_list *)kmalloc (sizeof (struct tty_list));
	tmp->tty = tty;
	tmp->next = free_tty;
	free_tty = tmp;
}

// Function for getting new id
uint32 GetFreeTtyID ()
{
	uint32 tty = pop_tty();
	if (tty > 0)
		return tty;
	return next_tty++;
}

static thread_t * sfind_last_task (tty_t * tty)
{
	if (!tty)
		return NULL;
	//if (!tty->window)
	//	return NULL;
	thread_t * ttmp = NULL;
	task_t * ptmp = (task_t *)ready_queue;
	while (ptmp)
	{
		ttmp = ptmp->threads;
		while (ttmp)
		{
			if (ttmp->terminal/*->window->pid == tty->window->pid*/)
				break;
			ttmp = ttmp->next;
		}
		if (ttmp)
			break;
		ptmp = ptmp->next;
	}
	if (!ttmp)
		return NULL;
		
	tty_t * ttytmp = ttmp->terminal;
	while (ttytmp->busy_screen)
	{
		ttmp = ttytmp->busy_screen;
		ttytmp = ttmp->terminal;
	}
	if (ttmp->terminal != tty)
		return ttmp;
	return NULL;
}

static thread_t * kfind_last_task (tty_t * tty)
{
	if (!tty)
		return NULL;
	//if (!tty->window)
	//	return NULL;
	thread_t * ttmp = NULL;
	task_t * ptmp = (task_t *)ready_queue;
	while (ptmp)
	{
		ttmp = ptmp->threads;
		while (ttmp)
		{
			if (ttmp->terminal/*->window->pid == tty->window->pid*/)
				break;
			ttmp = ttmp->next;
		}
		if (ttmp)
			break;
		ptmp = ptmp->next;
	}
	if (!ttmp)
		return NULL;
	
	tty_t * ttytmp = ttmp->terminal;
	while (ttytmp->busy_keyb)
	{
		ttmp = ttytmp->busy_keyb;
		ttytmp = ttmp->terminal;
	}
	if (ttmp->terminal != tty)
		return ttmp;
	return NULL;
}

void set_window (tty_t * tty, task_t * window)
{
	if (!tty)
		return;
	tty->window = window;
}

void set_name (tty_t * tty, char * name)
{
	if (!tty)
		return;
	if (!name)
	{
		sprintf(tty->name,"tty%d", GetFreeTtyID());
	}
	else
	{
		strcpy (tty->name, name);
	}
}

void occupy_screen (thread_t * thread)
{
	if (!thread)
		return;
	if (!thread->terminal)
		create_tty (thread, NULL);
	thread->terminal->prev_screen = sfind_last_task (thread->terminal);
	if (thread->terminal->prev_screen)
		thread->terminal->prev_screen->terminal->busy_screen = thread;
}

void occupy_keyboard (thread_t * thread)
{
	if (!thread)
		return;
	dprintf ("[occupy_keyboard] thread->terminal: 0x%x...\n", thread->terminal);
	if (!thread->terminal)
	{
		create_tty (thread, NULL);
	}
	thread->terminal->prev_keyb = kfind_last_task (thread->terminal);
	if (thread->terminal->prev_keyb)
		thread->terminal->prev_keyb->terminal->busy_keyb = thread;
}

void free_screen (const tty_t * tty)
{
	if (!tty)
		return;
	thread_t * tmp = tty->prev_screen;
	if (tmp && tmp->terminal)
		tmp->terminal->busy_screen = tty->busy_screen;
}

void free_keyboard (const tty_t * tty)
{
	if (!tty)
		return;
	thread_t * tmp = tty->prev_keyb;
	if (tmp && tmp->terminal)
		tmp->terminal->busy_keyb = tty->busy_keyb;
}

void create_tty (thread_t * thread, char * name/*, task_t * window*/)
{
	if (!thread)
		return NULL;
	if (thread->terminal)
		return;
	dprintf ("[create_tty] thread->terminal: 0x%x...\n", thread->terminal);
	thread->terminal = (tty_t *) kmalloc (sizeof(tty_t));
	dprintf ("[create_tty] thread->terminal: 0x%x...\n", thread->terminal);
	set_name (thread->terminal, name);
	dprintf ("[create_tty] thread->terminal->name: %s...\n", thread->terminal->name);
	thread->terminal->window = NULL; //set_window (thread->terminal, NULL/*window*/);
	thread->terminal->busy_screen = NULL;
	thread->terminal->busy_keyb = NULL;
	thread->terminal->prev_screen = NULL;
	thread->terminal->prev_keyb = NULL;
	// ???
	occupy_screen (thread);
	occupy_keyboard (thread);
}

void delete_tty (thread_t * thread)
{
	if (!thread)
		return;
	if (!thread->terminal)
		return;
	free_screen (thread->terminal);
	free_keyboard (thread->terminal);
	push_tty (thread->terminal->name);
	kfree (thread->terminal);
	thread->terminal = NULL;
}

bool IsAccess2Key (thread_t * thread)
{
	if (!ready_queue && !current_task)
		return true;
	
	if (!thread)
		return false;
	
	if (thread->terminal)
	{
		if (!thread->terminal->busy_keyb)
			return true;
		else
			return false;
	}
	
	if (thread != thread->parent->threads)
	{
		if (!thread->parent->threads->terminal->busy_keyb)
			return true;
		else
			return false;
	}
	
	while (thread->parent && !thread->parent->threads->terminal) thread = thread->parent->threads;
	
	if (!thread->parent)
		return false;
	
	if (thread->parent->threads->terminal)
	{
		if (!thread->parent->threads->terminal->busy_keyb)
			return true;
	}
	return false;
}

bool IsAccess2Scr (thread_t * thread)
{
	if (!ready_queue && !current_task)
		return true;
	
	if (!thread)
		return false;
	
	if (thread->terminal)
	{
		if (!thread->terminal->busy_screen)
			return true;
		else
			return false;
	}
	
	if (thread != thread->parent->threads)
	{
		if (!thread->parent->threads->terminal->busy_screen)
			return true;
		else
			return false;
	}
	
	while (thread->parent && !thread->parent->threads->terminal) thread = thread->parent->threads;
	
	if (!thread->parent)
		return false;
	
	if (thread->parent->threads->terminal)
	{
		if (!thread->parent->threads->terminal->busy_screen)
			return true;
	}
	return false;
}